package com.qcrud;

import com.qcrud.core.*;
import com.qcrud.core.result.QueryResult;
import com.qcrud.core.transaction.TransactionIsolationLevel;
import com.qcrud.exception.QcrudException;
import com.qcrud.mapper.MapperRegistry;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;

public class Qcrud {
    private final ConnectionFactory connectionFactory;
    private final MapperRegistry mapperRegistry = new MapperRegistry();


    private Qcrud(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public static Qcrud create(DataSource dataSource) {
        try {
            return create(dataSource.getConnection());
        } catch (SQLException e) {
            throw new QcrudException("get connection error", e);
        }
    }

    public static Qcrud create(Connection connection) {
        return new Qcrud(new SingleConnectionFactory(connection));
    }

    public Connection getConnection() {
        return connectionFactory.openConnection();
    }

    public static Qcrud create(final String url, final String username, final String password) {
        Objects.requireNonNull(url, "null url");
        Objects.requireNonNull(username, "null username");
        Objects.requireNonNull(password, "null password");
        try {
            return create(DriverManager.getConnection(url, username, password));
        } catch (SQLException e) {
            throw new QcrudException("get connection error", e);
        }
    }

    public Handle open() {
        return new Handle(this.connectionFactory);
    }

    public Object execute(SqlMetaData sqlMetaData, Object[] args) {
        try {
            Handle handle = open();
            // 测试先写死
            QueryResult queryResult = handle.createQuery(sqlMetaData.sql(), args[0]);
            return null != queryResult ? queryResult.toMapList().get(0).get("name") : null;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public <T> T withMapper(Class<T> type) {
        return mapperRegistry.newInstance(this, type);
    }

    public <R, X extends Exception> R withHandle(HandleCallback<R, X> callback) throws X {
        return callback.withHandle(this.open());
    }

    public <X extends Exception> void useHandle(final HandleConsumer<X> consumer) throws X {
        withHandle(consumer.asCallback());
    }

    public <R, X extends Exception> R inTransaction(final HandleCallback<R, X> callback) throws X {
        return this.open().inTransaction(callback);
    }

    public <X extends Exception> void useTransaction(final HandleConsumer<X> consumer) throws X {
        inTransaction(consumer.asCallback());
    }

    public <R, X extends Exception> R inTransaction(TransactionIsolationLevel level, HandleCallback<R, X> callback) throws X {
        return new Handle(this.connectionFactory).inTransaction(level, callback);
    }

    public <X extends Exception> void useTransaction(TransactionIsolationLevel level, HandleConsumer<X> consumer) throws X {
        inTransaction(level, consumer.asCallback());
    }
}
